package org.leumasjaffe.initiativeTracker.operator;

import static org.leumasjaffe.initiativeTracker.operator.InitiativeController.tracker;

import java.awt.Color;
import java.io.PrintStream;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.leumasjaffe.initiativeTracker.Constants;
import org.leumasjaffe.initiativeTracker.entity.Character;
import org.leumasjaffe.initiativeTracker.entity.NPC;
import org.leumasjaffe.initiativeTracker.entity.PC;
import org.leumasjaffe.initiativeTracker.entity.Status;
import org.leumasjaffe.initiativeTracker.view.CharacterPanel;
import org.leumasjaffe.initiativeTracker.view.InitiativeFrame;

import org.leumasjaffe.util.io.PortableScanner;

@NonNullByDefault
public class CharacterLinker implements Comparable<CharacterLinker>{	
//	public static final CharacterLinker blank = new CharacterLinker() {
//		public void update(int pos) {}
//		public void sizeName() {}
//		public void tearDown() {}
//		public boolean damage(int dmg, boolean tmp) {return false;}
//		public boolean checkHealth(int dmg) {return true;}
//	};
	
	private final String id;
	private Character character;
	private CharacterPanel charPanel;

	public CharacterLinker(Character character, String gameId){
		this.id = gameId;
		this.character = character;
		if (Constants.client) {
			Color outline;
			if (character instanceof PC)
				outline = Color.BLACK;
			else if (((NPC) character).isAllied())
				outline = Color.CYAN;
			else
				outline = Color.RED;
			this.charPanel = new CharacterPanel(character.getDis(), outline);

			this.charPanel.setNameText(character.getName());

			for (int i = 0; i < character.getStatus().length; i++)
				this.charPanel.arrayCheckbox[i].setSelected(character.getStatus()[i]);

			if (character.notObs()) {
				this.charPanel.setWillText("" + character.getWill());
				this.charPanel.setRefText("" + character.getRef());
				this.charPanel.setFortText("" + character.getFort());
				this.charPanel.setArmorText("" + character.getAc());
			}
			else {
				this.charPanel.setWillText("??");
				this.charPanel.setRefText("??");
				this.charPanel.setFortText("??");
				this.charPanel.setArmorText("??");
			}

			character.setDis(this.charPanel.sizeName(character.getName()));
		}
	}
	
//	private CharacterLinker() {}

	@SuppressWarnings("null")
	public Character getCharacter() {
		return this.character;
	}
	
	/**
	 * A function for the adding a character to the GUI and updating its data
	 * @param pos The position on the page
	 * @param gui The GUI for characters to be placed onto
	 */
	public void update(int pos) {
		if (!Constants.client) return;
		int hp = this.character.getHp();
		int maxHP = this.character.getMaxHP();
				
		this.charPanel.setBounds(0, 50*pos+59, this.charPanel.getLength(), 50);
		this.charPanel.update();
		
		if (this.character.notObs() || (this.character instanceof NPC && ((NPC) this.character).isAllied()))
			this.charPanel.setHPText(hp + "/" + maxHP);
		else if (this.character instanceof PC && ((PC) this.character).getTemp() > 0) {
			this.charPanel.setHPText("(" + (this.character.getHp() + ((PC) this.character).getTemp()) + ")"
					+ "/" + this.character.getMaxHP()); //Use parenthesis to let us know
		}
		else
			this.charPanel.setHPText("(" + (maxHP - hp) + ")");
		
		this.character.readStatus(this.charPanel.statusAsBitString());
				
		InitiativeFrame.getInstance().add(this.charPanel);
		this.charPanel.setVisible(true);
		this.charPanel.repaint();
		InitiativeFrame.getInstance().repaint();
	}
	
	/**
	 * A helper function to resize the name Button in order to allow multi-line text if necessary
	 */
	public void sizeName() {
		this.character.setDis(this.charPanel.sizeName(this.character.getName()));
	}

	/**
	 * Removes all Components from the given GUI
	 * @param d The GUI that we want to remove this Character's components from
	 */
	public void tearDown() {
		if (!Constants.client) return;
		InitiativeFrame.getInstance().remove(this.charPanel);
		this.charPanel.setVisible(false);
		this.charPanel.repaint();
	}
	
	public void export(PrintStream out) {
		this.character.export(out);
		out.println("END OF CHARACTER");
	}
	
	/**
	 * Checks to see if this Character is taking ongoing damage, deals it, and checks that the character is still alive
	 * @return Returns false if the Character has died
	 */
	public boolean bleed() {
		int dot = this.character.getDot();
		if (this.charPanel.isBleeding()) {
			if (dot == 0 || this.charPanel.renewOngoing()) {
				dot = PortableScanner.nextInt("How much damage does this bleed do");
				this.character.setDot(dot);
			}
			this.character.setHp(this.character.getHp() - dot);
			System.out.println(this.character.getName() + " suffers " + dot + " ongoing damage.");
			System.out.println();
		}
		else {
			dot = 0;
		}
		return checkHealth(dot);
	}
	
	@SuppressWarnings("null")
	public boolean damage(int dmg, boolean temp) {
		Character c = this.getCharacter();
		if (dmg < 0) {
			if (c instanceof PC) {
				if (temp)
					((PC) c).setTemp(((PC) c).getTemp() - dmg);
				else 
					((PC) c).recover(-dmg);
			}
			else
				c.setHp(c.getHp() - dmg);
		}
		else {
			if (c instanceof PC) {
				int t = ((PC) c).getTemp();
				((PC) c).setTemp(t - dmg);
			}
			else
				c.setHp(c.getHp() - dmg);
		}
		
		if (!checkHealth(dmg)) {
			tracker(this.id).list.remove(this);
			return true;
		}
		else
			return false;
	}
	
	public boolean checkHealth(int damage) {
		String name = this.character.getName();
		double currentHP = (double)this.character.getHp()/(double)this.character.getMaxHP();
		if (currentHP <= 0.0) {
			if (this.character instanceof PC && currentHP > -0.5) {
				collapse();
				System.out.println("--------------------");
				System.out.println(name + " has collapsed!");
				System.out.println("--------------------");
			}
			else {
				System.out.println("--------------------");
				System.out.println(name + " has perished!");
				System.out.println("--------------------");
				return false;
			}
		}
		else if (currentHP <= 0.5 && currentHP + damage > 0.5) {
			System.out.println("--------------------");
			System.out.println(name + " is bloodied!");
			System.out.println("--------------------");
		}
		else if (currentHP > 0.0 && currentHP - damage <= 0.0) {
			awaken();
		}
		return true;
	}
	
	private void collapse() {
		this.charPanel.getUnconscious().setSelected(true);
	}
	
	private void awaken() {
		this.charPanel.getUnconscious().setSelected(false);
	}
	
	/**
	 * Lists the ailments that this Character has
	 */
	public void listAilments() {
		String name = this.character.getName();
		int dot = this.character.getDot();
		int n = 0;
		System.out.println("--------------------");
		System.out.println("It is " + name + "'s turn.");
		double d = ((double)this.character.getHp())/((double)this.character.getMaxHP());;
		if (d <= 0.5)
			System.out.println(name + " is Bloodied!");
		System.out.println("At the end of the turn, " + name +
		" may be able to make a saving throw against one of the following effects:");
		System.out.println("--------------------");
		for (int i = 0; i < this.charPanel.getNumStat(); i++) {
			if (this.charPanel.getStatus(i)) {
				n++;
				if (i == 14)
					System.out.println(n + ") " + name + Status.meaning(i, dot) + "\n");
				else
					System.out.println(n + ") " + name + " is " + Status.allStatus[i].getName()
							+ Status.meaning(i, dot) + "\n");
			}
		}
		if (n==0)
			System.out.println(name + " has no status effects on them!");
		System.out.println();
	}
	

	/**
	 * Function for healing that uses healing surges and needs input
	 * @return 
	 */
	public static float heal(PC c) {
		if (c.getSurgeLeft() == 0)
			System.out.println(c.getName() + " is out of Healing Surges.");
		//If we have no surges, then there is a problem
		else {
			System.out.print("Bonus healing: "); //How much extra do we get?
			String s = PortableScanner.nextLine();
			try {
				int bonus = Integer.valueOf(s);
				return c.surge(bonus);
			}
			catch (NumberFormatException nfe) {
				// This lets us see if a potion name was entered instead of a number
				int surgeBase = c.getSurgevalue();
				if (s.equalsIgnoreCase("PoH") || s.equalsIgnoreCase("heal")
						|| s.equalsIgnoreCase("healing")) {
					return c.surge(10-surgeBase);
				}
				else if (s.equalsIgnoreCase("PoV") || s.equalsIgnoreCase("vital")
						|| s.equalsIgnoreCase("vitality")) {
					System.out.println("Please make a saving throw");
					return c.surge(25-surgeBase);
				}
				else if (s.equalsIgnoreCase("PoR") || s.equalsIgnoreCase("recover")
						|| s.equalsIgnoreCase("recovery")) {
					System.out.println("Please make a saving throw against" +
							" every effect that a save can end");
					return c.surge(50-surgeBase);
				}
			}
		}
		return -0.5F;
	}
	
	public boolean isTarget() {
		return this.charPanel.isTarget();
	}

	@SuppressWarnings("null")
	public CharacterPanel getCharPanel() {
		return this.charPanel;
	}

	@Override
	public int compareTo(@Nullable CharacterLinker arg0) {
		if (arg0 == null) return -1;
		return this.character.compareTo(arg0.getCharacter());
	}
	
}
